package com.yfq.springframework.core.convert.support;

import com.yfq.springframework.core.convert.ConversionService;
import com.yfq.springframework.core.convert.converter.Converter;
import com.yfq.springframework.core.convert.converter.ConverterFactory;
import com.yfq.springframework.core.convert.converter.ConverterRegistry;
import com.yfq.springframework.core.convert.converter.GenericConverter;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;

/**
 * @author 姚凤桥
 * @date 2023/2/11 11:45
 * @description 标准的类型转换服务，也是一个转换器注册器
 */
public class GenericConversionService implements ConversionService, ConverterRegistry {
    private final Map<GenericConverter.ConvertiblePair, GenericConverter> converters = new HashMap<>();

    @Override
    public void addConverter(Converter<?, ?> converter) {
        GenericConverter.ConvertiblePair typeInfo = getRequiredTypeInfo(converter);
        ConverterAdapter converterAdapter = new ConverterAdapter(typeInfo, converter);
        for (GenericConverter.ConvertiblePair convertibleType : converterAdapter.getConvertibleTypes()) {
            converters.put(convertibleType, converterAdapter);
        }
    }

    @Override
    public void addConverter(GenericConverter converter) {
        for (GenericConverter.ConvertiblePair convertibleType : converter.getConvertibleTypes()) {
            converters.put(convertibleType, converter);
        }
    }

    @Override
    public void addConverterFactory(ConverterFactory<?, ?> converterFactory) {
        GenericConverter.ConvertiblePair typeInfo = getRequiredTypeInfo(converterFactory);
        ConverterFactoryAdapter converterFactoryAdapter = new ConverterFactoryAdapter(typeInfo, converterFactory);
        for (GenericConverter.ConvertiblePair convertibleType : converterFactoryAdapter.getConvertibleTypes()) {
            converters.put(convertibleType, converterFactoryAdapter);
        }
    }

    @Override
    public boolean canConversion(Class<?> sourceType, Class<?> targetType) {
        return getConverter(sourceType, targetType) != null;
    }


    @Override
    public <T> T conversion(Object source, Class<T> tClass) {
        GenericConverter converter = getConverter(source.getClass(), tClass);
        return (T) converter.convert(source, source.getClass(), tClass);
    }

    /**
     * 根据源类型、目标类型获取转换器
     */
    private GenericConverter getConverter(Class<?> sourceType, Class<?> targetType) {
        List<Class<?>> sourceCandidates = getClassHierarchy(sourceType);
        List<Class<?>> targetCandidates = getClassHierarchy(targetType);

        for (Class<?> sourceCandidate : sourceCandidates) {
            for (Class<?> targetCandidate : targetCandidates) {
                GenericConverter.ConvertiblePair typeInfo = new GenericConverter.ConvertiblePair(sourceCandidate, targetCandidate);

                GenericConverter genericConverter = converters.get(typeInfo);
                if (genericConverter != null) {
                    return genericConverter;
                }
            }
        }

        return null;
    }

    /**
     * 获取所有的父类
     */
    private List<Class<?>> getClassHierarchy(Class<?> clazz) {
        List<Class<?>> list = new ArrayList<>();

        while (clazz != null) {
            list.add(clazz);

            clazz = clazz.getSuperclass();
        }

        return list;
    }

    private GenericConverter.ConvertiblePair getRequiredTypeInfo(Object object) {
        //得到所有实现的接口类型
        Type[] types = object.getClass().getGenericInterfaces();
        ParameterizedType parameterizedType = (ParameterizedType) types[0];
        //得到接口的泛型
        Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
        Class<?> sourceType = (Class<?>) actualTypeArguments[0];
        Class<?> targetType = (Class<?>) actualTypeArguments[1];

        return new GenericConverter.ConvertiblePair(sourceType, targetType);
    }

    private final class ConverterAdapter implements GenericConverter {

        private final ConvertiblePair typeInfo;

        private final Converter<Object, Object> converter;

        public ConverterAdapter(ConvertiblePair typeInfo, Converter<?, ?> converter) {
            this.typeInfo = typeInfo;
            this.converter = (Converter<Object, Object>) converter;
        }

        @Override
        public Set<ConvertiblePair> getConvertibleTypes() {
            return Collections.singleton(typeInfo);
        }

        @Override
        public Object convert(Object source, Class sourceType, Class targetType) {
            return converter.convert(source);
        }
    }

    private final class ConverterFactoryAdapter implements GenericConverter {

        private final ConvertiblePair typeInfo;

        private final ConverterFactory<Object, Object> converterFactory;

        public ConverterFactoryAdapter(ConvertiblePair typeInfo, ConverterFactory<?, ?> converterFactory) {
            this.typeInfo = typeInfo;
            this.converterFactory = (ConverterFactory<Object, Object>) converterFactory;
        }

        @Override
        public Set<ConvertiblePair> getConvertibleTypes() {
            return Collections.singleton(typeInfo);
        }

        @Override
        public Object convert(Object source, Class sourceType, Class targetType) {
            return converterFactory.getConverter(targetType).convert(source);
        }
    }

}
